package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSON;

import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.gateway.properties.AuthUrlProperties;
import com.atguigu.gmall.user.entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;


@Component
@Slf4j
public class UserAuthGateAwayFilter implements GlobalFilter {
    @Autowired
    private  StringRedisTemplate stringRedisTemplate;
    @Autowired
    private AuthUrlProperties authUrlProperties;

    AntPathMatcher  pathMatcher=new AntPathMatcher();

    /**
     *
     * @param exchange 包含了请求和响应
     * @param chain  filter链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange,
                             GatewayFilterChain chain) {
         ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        log.info("请求开始{}",path);
        //TODO 静态资源放行 无需查看用户信息
        long count = authUrlProperties.getAnyoneUrl()
                .stream()
                .filter(pattern ->
                pathMatcher.match(pattern, path))
                .count();
        if(count>0){
            log.info("静态资源正在放行");
            return chain.filter(exchange);
        }
        //TODO 直接拒绝访问路径
        long denyCount = authUrlProperties.getDenyUrl()
                .stream()
                .filter(pattern -> pathMatcher.match(pattern, path))
                .count();
        if(denyCount>0){
            log.warn("疑似攻击请求，打回------");
            Result<String> result=Result.build("", ResultCodeEnum.PERMISSION);
            //返回错误json
            return  responseJson(exchange,result);
        }

        //TODO 有权限才能访问  必须在登录之后，才能访问
        long authCount = authUrlProperties.getAuthUrl()
                .stream()
                .filter(pattern -> pathMatcher.match(pattern, path))
                .count();
        if(authCount>0){
        //必须登录了才能访问
            //TODO 拿到令牌之后才能算是登录了
            String token = getToken(exchange);
            UserInfo userInfo = getUserInfo(token);
            if(StringUtils.isEmpty(token)||userInfo==null){
                //TODO 浏览器重定向去登录页面
                return redirectToPage(exchange, authUrlProperties.getLoginPage());
            }

        }
        log.info("验证用户信息开始。。。透传用户id");
        //TODO 获取请求头带来的token
        // TODO 获取redis中登录的用户信息
        UserInfo userInfo = getUserInfo(getToken(exchange));
        //TODO 拿到用户信息 我们就透传 id
         return  userIdThrougth(chain,exchange,userInfo);

//        //1获取请求头或者cookie中携带的用户令牌 标识
//    //     String token=  getToken(exchange);
////        //2根据请求获取的token 获取redis中的登录的用户信息
//         UserInfo info= getUserInfo(token);
//
//      Mono<Void> filter = chain.filter(exchange);
//        return filter;

    }

    /**
     * todo 拿到用户信息 进行透传
     * @param chain
     * @param exchange
     * @param userInfo
     * @return
     */
    private Mono<Void> userIdThrougth(GatewayFilterChain chain, ServerWebExchange exchange, UserInfo userInfo) {
     // 1透传 请求的数据都是只读不能修改
        ServerHttpRequest.Builder reBuilder = exchange.getRequest().mutate();
         //用户id在请求头
        if(userInfo!=null){
            log.info("用户信息放在头中，向下透传");
            //只要构造完成 老的exchange里面的请求也会跟着变
            reBuilder.header(RedisConst.USER_ID_HEADER,userInfo.getId().toString()).build();
        }
        //3临时id
         String tempId=  getTempId(exchange);
        //TODO 犯的错误 网关没有传递下去
        if(!StringUtils.isEmpty(tempId)){
            log.info("用户的临时信息放在头，向下透传");
            // TODO 只要构造完成 老的exchange里面请求也会跟着变
            reBuilder.header(RedisConst.TEMP_ID_HEADER,tempId).build();
        }
        //  4 放行
         return  chain.filter(exchange);
    }

    /**
     * TODO 用户临时id的透传
     * @param exchange
     * @return
     */
    private String getTempId(ServerWebExchange exchange) {
        //拿到请求
        ServerHttpRequest request = exchange.getRequest();
        //先看请求头
        String token = request.getHeaders().getFirst("userTempId");
        if(!StringUtils.isEmpty(token)){
            return  token;
        }
       //请求头中没有看cookie
        HttpCookie cookie = request.getCookies().getFirst("userTempId");
        if(cookie!=null){
            return  cookie.getValue();
        }
     return  null;
    }

    /**
     * 重定向到登录页面
     * @param exchange
     * @param loginPage
     * @return
     */
    private Mono<Void> redirectToPage(ServerWebExchange exchange, String loginPage) {
        ServerHttpResponse response = exchange.getResponse();
        //当时的请求路径
        URI uri = exchange.getRequest().getURI();
        //设置好要跳转的路径
        loginPage+="?originUrl="+uri.toString();
        //设置响应状态码
        response.getHeaders().set("Location",loginPage);
        return response.setComplete();//响应结束
    }

    /**
     * TODO 响应的json
     * @param exchange
     * @param result
     * @return
     */
    private Mono<Void> responseJson(ServerWebExchange exchange, Result result) {
        //得到响应对象
        ServerHttpResponse response = exchange.getResponse();
        //得到数据的DataBuffer
        String json = JSON.toJSONString(result);
        DataBuffer dataBuffer = response
                              .bufferFactory()
                              .wrap(json.getBytes(StandardCharsets.UTF_8));
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);//请求头类型
        return  response.writeWith(Mono.just(dataBuffer));
    }

    /**
     * 根据token获取redis中的信息
     * @param token
     * @return
     */

    private UserInfo getUserInfo(String token) {
        if(StringUtils.isEmpty(token)){
            return  null ;
        }
        String json = stringRedisTemplate.opsForValue().get("login:user:"+token);
         if(StringUtils.isEmpty(json)){
            return null;
         }
        UserInfo userInfo = JSON.parseObject(json, UserInfo.class);
        return  userInfo;
    }

    /**
     * 获取请求头中或者cookie中的令牌标识
     * @param exchange
     * @return
     */
    private String getToken(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        //TODO 先看请求头
        String token = request.getHeaders().getFirst("token");
        if(!StringUtils.isEmpty(token))  {
            return token;
        }
        //TDDO 再看cookie
        HttpCookie cookie = request.getCookies().getFirst("token");

        if(!StringUtils.isEmpty(cookie)) {
           return cookie.getValue();
        }
           return  null;
    }
}
